//
// Created by yanhai on 2020/2/21.
//

#pragma once

#include "utility/string.hpp"
#include "utility/urldecode.hpp"
#include "http.hpp"
#include "formparser.hpp"
#include "exception.hpp"

class BaseRequest {
    using form_data_parser_class = FormDataParser;

public:
    explicit BaseRequest(struct evhttp_request *request);

    class Uri {
    public:
        explicit Uri(const evhttp_request *request)
        {
            _raw_uri = evhttp_request_get_uri(request);
            auto __uri = evhttp_request_get_evhttp_uri(request);
            _path = evhttp_uri_get_path(__uri);
            auto __raw_query = evhttp_uri_get_query(__uri);
            _raw_query = __raw_query != nullptr ? __raw_query : "";
            parse_query();
        }

        const std::string &uri() const
        {
            return _raw_uri;
        }

        const std::string &path() const
        {
            return _path;
        }

        const form_type &query() const
        {
            return _query;
        }

    private:
        void parse_query()
        {
            if (_raw_query.empty())
                return;
            auto v = utility::string::split(_raw_query, '&');
            for (auto &item: v) {
                auto p = utility::string::split(item, '=');
                _query.insert(utility::URLDecode(p[0]), utility::URLDecode(p[1]));
            }
        }

    private:
        std::string _raw_uri;
        std::string _path;
        std::string _raw_query;
        form_type _query;
    };

    int method() const
    {
        return _method;
    }

    const std::string &user_agent() const
    {
        return _user_agent;
    }

    const form_type &form() const
    {
        return _form;
    }

    const file_type &file() const
    {
        return _files;
    }

protected:
    void _load_form_data();

    form_data_parser_class make_form_data_parser();

    std::string &_get_stream_for_parsing();

    void _get_stream();

public:
    Uri uri;

protected:
    evhttp_request *request;
    evkeyvalq *headers;
    std::string content_type;
    int content_length;
    std::string _body;

private:
    bool want_form_data_parsed;
    std::string _user_agent;
    int _method;
    form_type _form;
    file_type _files;
};

// detail

inline BaseRequest::BaseRequest(struct evhttp_request *request) : request(request), uri(request)
{
    want_form_data_parsed = true;
    _method = evhttp_request_get_command(request);
    headers = evhttp_request_get_input_headers(request);
    content_type = evhttp_get_header_value(headers, "Content-Type");
    _user_agent = evhttp_get_header_value(headers, "User-Agent");
    content_length = get_content_length(headers);
}

inline void BaseRequest::_load_form_data()
{
    if (!want_form_data_parsed) {
        return;
    }

    want_form_data_parsed = false;

    // todo parse form data
    auto r = parse_options_header(content_type);
    auto &mimetype = std::get<0>(r);
    auto &options = std::get<1>(r);

    auto parser = make_form_data_parser();
    auto r2 = parser.parse(_get_stream_for_parsing(), mimetype, content_length, options);
    _form = std::get<0>(r2);
    _files = std::get<1>(r2);
}

BaseRequest::form_data_parser_class BaseRequest::make_form_data_parser()
{
    return form_data_parser_class();
}

std::string &BaseRequest::_get_stream_for_parsing()
{
    return _body;
}

void BaseRequest::_get_stream()
{
    _body.clear();
    char buf[1024] = {0};
    auto in_buf = evhttp_request_get_input_buffer(request);
    while (evbuffer_get_length(in_buf)) {
        auto len = evbuffer_remove(in_buf, buf, sizeof(buf) - 1);
        if (len > 0) {
            buf[len] = 0;
            _body.append(buf, len);
        }
    }
}
